import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
import type { Result } from "@/api/http/types";
import axios from "axios";
import { createVNode } from "vue";
import { ExclamationCircleOutlined } from "@ant-design/icons-vue";
import { ContentTypeEnum, MethodTypeEnum, ResultCodeEnum } from "@/enums/http";

export const NO_TOKEN_API = ["/login"];
export const TOKEN_PREFIX = "Bearer ";

// 用于存储请求的标识，便于路由切换时取消请求(仅取消请求不返回响应，并不会截断响应，即前端取消了请求, 实质后端还是会响应的)
const cancelTokenStore: any = {
  source: {
    token: null,
    cancel: null
  }
};

class Request {
  // 原生 axios 实例对象
  axiosInstance: AxiosInstance;

  // 构造函数会在对象创建时执行
  constructor(config: AxiosRequestConfig) {
    // 初始化axios实例
    this.axiosInstance = axios.create(config);

    // request拦截器
    this.axiosInstance.interceptors.request.use(
      config => {
        // 防止GET请求缓存而追加时间戳
        if (config.method?.toUpperCase() === "GET") {
          config.params = { ...config.params, _t: new Date().getTime() };
        }
        config.timeoutErrorMessage = "请求超时";
        config.cancelToken = cancelTokenStore.source.token;

        const userStore = useUserStore();
        const token = userStore.token;
        const headers = !NO_TOKEN_API.includes(config.url!)
          ? {
              "Content-Type": ContentTypeEnum.JSON,
              Authorization: `${TOKEN_PREFIX}${token}`
            }
          : null;

        return {
          ...config,
          headers
        };
      },
      error => {
        console.log("request: ", error);
        return Promise.reject(error);
      }
    );

    // response拦截器
    this.axiosInstance.interceptors.response.use(
      (response: AxiosResponse<Result>) => {
        //发生错误返回错误信息
        if (axios.isAxiosError(response)) {
          return Promise.reject(response);
        }
        if ("status" in response && response.status !== 200) {
          const status = response.status || 0;
          antdNotification.error({
            message: "错误提示",
            description: getStatusMsg(status)
          });
          return Promise.reject(response);
        }

        //返回成功的响应数据
        return response;
      },
      error => {
        //取消请求，不报错并返回空值
        if (axios.isCancel(error)) {
          return;
        }

        if (axios.isAxiosError(error)) {
          const status = error.request.status || 0;
          if (status === 401) {
            const userStore = useUserStore();
            antdModal.destroyAll();
            antdModal.confirm({
              title: "系统提示",
              icon: createVNode(ExclamationCircleOutlined),
              content: createVNode("div", { style: "color:red;" }, "登录状态已过期，请重新进行登录!"),
              onOk() {
                userStore.logout().then(() => {
                  antdMessage.success("退出成功!");
                  location.href = "/#/login";
                });
              }
            });
          } else {
            antdNotification.error({
              message: "错误提示",
              description: getStatusMsg(status)
            });
          }
        }

        return Promise.reject(error);
      }
    );
  }

  request<T = any>(config: AxiosRequestConfig) {
    return new Promise<Result<T>>((resolve, reject) => {
      this.axiosInstance(config)
        .then((response: AxiosResponse<Result<T>>) => {
          if (response.data.code == ResultCodeEnum.FAIL) {
            antdMessage.error(response.data.message);
            reject(response.data.message);
          } else {
            resolve(response.data);
          }
        })
        .catch(err => {
          console.log(err);
          reject(err);
        });
    });
  }

  /**
   * GET类型的网络请求
   */
  get<T = any>(config: AxiosRequestConfig) {
    config.method = MethodTypeEnum.GET;
    return this.request<T>(config);
  }

  /**
   * POST类型的网络请求
   */
  post<T = any>(config: AxiosRequestConfig) {
    config.method = MethodTypeEnum.POST;
    return this.request<T>(config);
  }

  /**
   * PUT类型的网络请求
   */
  put<T = any>(config: AxiosRequestConfig) {
    config.method = MethodTypeEnum.PUT;
    return this.request<T>(config);
  }

  /**
   * DELETE类型的网络请求
   */
  delete<T = any>(config: AxiosRequestConfig) {
    config.method = MethodTypeEnum.DELETE;
    return this.request<T>(config);
  }
}

export default Request;

/**
 * 获取状态信息
 * @param status
 */
function getStatusMsg(status: number) {
  let message = "";
  switch (status) {
    case 400:
      message = "请求错误";
      break;
    case 401:
      message = "授权失效，请重新登录";
      break;
    case 403:
      message = "没有权限";
      break;
    case 404:
      message = "请求出错";
      break;
    case 405:
      message = "请求方法未允许";
      break;
    case 408:
      message = "请求超时";
      break;
    case 500:
      message = "服务器错误";
      break;
    case 501:
      message = "服务未实现";
      break;
    case 502:
      message = "网络错误";
      break;
    case 503:
      message = "服务不可用";
      break;
    case 504:
      message = "网络超时";
      break;
    case 505:
      message = "Http版本不支持该请求";
      break;
    default:
      message = `未知错误[${status}]`;
  }

  return message;
}

/**
 * 清空本实例所有请求（通常在路由跳转时调用）
 */
export function clearAllPending() {
  const CancelToken = axios.CancelToken;
  cancelTokenStore.source.cancel && cancelTokenStore.source.cancel();
  cancelTokenStore.source = CancelToken.source();
}
